package ljl.alg.jianzhioffer.round2;

import java.util.HashMap;
import java.util.Map;

public class _20_parse_number {
    
    /**
     * 搞得我想退出编程圈
     */
    class very_ugly {
        String s;
        int pos;
        
        public boolean isNumber(String s) {
            s = s.trim();
            if (s.length() == 0) return false;
            this.s = s;
            
            boolean ok = true;
            boolean atLeastOne = false;
            
            if (peek('+', '-'))
                scanSign();
            
            // 数字
            if (peekIn('0', '9')) {
                atLeastOne = true;
                scanInt();
            }
            
            
            // 点或 e 或 没东西
            if (peek('.')) {
                
                advance();
                atLeastOne |= scanInt();
                if (peek('e', 'E'))
                    ok = scanEPart();
            } else if (peek('e', 'E')) {
                ok = scanEPart();
            }
            return atLeastOne && ok && eof();
        }
        
        boolean scanEPart() {
            advance();
            if (peek('+', '-'))
                scanSign();
            return scanInt();
        }
        
        boolean eof() {
            return pos == s.length();
        }
        
        void advance() {
            pos++;
        }
        
        boolean peekIn(char start, char end) {
            if (pos >= s.length()) return false;
            char c = s.charAt(pos);
            return c >= start && c <= end;
        }
        
        boolean peek(char c) {
            if (pos >= s.length()) return false;
            return s.charAt(pos) == c;
        }
        
        boolean peek(char c1, char c2) {
            if (pos >= s.length()) return false;
            return s.charAt(pos) == c1 || s.charAt(pos) == c2;
        }
        
        void scanSign() {
            advance();
        }
        
        boolean scanInt() {
            int i = 0;
            while (pos < s.length() && Character.isDigit(s.charAt(pos))) {
                pos++;
                i++;
            }
            return i > 0;
        }
    }
    
    /**
     * 路飞状态机
     *
     * 我草他妈我哭了
     * 一个浮点数状态机就这么复杂，这让人如何掌控雷电
     *
     * 如果能自动生成这些代码还行，纯手工太费力了
     * 其实了解了状态机，还挺清晰明白的
     * */
    public boolean isNumber333(String s) {
        Map[] state = {
            // 需要注意的是，小数点这个状态；小数点不能单独出现，必须分成两个状态，前面有数和前面没数
            // 点后面是可以没数的，但是不能两遍都没数
            new HashMap<>() {{ put(' ', 0); put('s', 1); put('d', 2); put('.', 4); }}, // 0. 首部空白
            new HashMap<>() {{ put('d', 2); put('.', 4); }},                           // 1. e 前面的符号
            new HashMap<>() {{ put('d', 2); put('.', 3); put('e', 5); put(' ', 8); }}, // 2. 数字
            new HashMap<>() {{ put('d', 3); put('e', 5); put(' ', 8); }},              // 3. 可以跟数字的点
            new HashMap<>() {{ put('d', 3); }},                                        // 4. 必须跟数字的点
            new HashMap<>() {{ put('s', 6); put('d', 7); }},                           // 5. e
            new HashMap<>() {{ put('d', 7); }},                                        // 6. e 后面的符号
            new HashMap<>() {{ put('d', 7); put(' ', 8); }},                           // 7. e 后面的数字
            new HashMap<>() {{ put(' ', 8); }}                                         // 8. 尾部空白
        };
    
        int p = 0;
        // 找了很长时间原因，这里 char 写成 int 了
        // 虽然 char 和 int 值一样但是在 hashmap 里不一样。。不应该啊！
        char t;
        for(char c : s.toCharArray()) {
            if(c >= '0' && c <= '9') t = 'd';
            else if(c == '+' || c == '-') t = 's';
            else if(c == 'e' || c == 'E') t = 'e';
            else if(c == '.' || c == ' ') t = c;
            else t = 0;
            if(!state[p].containsKey(t)) return false;
            p = (int)state[p].get(t);
        }
        return p == 2 || p == 3 || p == 7 || p == 8;
    }
    
    
    /**
     * 我推崇的解法，递归下降
     * 递归你妈，哪有递归
     *
     * 我推崇的解法，没有递归只有下降
     *
     * 把 integer 分成 普通 integer 和 unsigned 来判断，真是妙哉
     *
     * c++ 代码不用一直判断是否越界是因为它的字符数组最后一个字符是 0，可以充当哨兵
     * java 没那玩意，所以很屌丝
     * */
    class very_good {
        int index;
        String str;
        public boolean isNumber(String s) {
            str = s;
        
            skipWs();
        
            boolean numeric = scanInteger();
            if (index < str.length() && str.charAt(index) == '.') {
                index++;
                // 这体现了小数点前后至少有有一处有数字
                numeric = scanUnsigned() || numeric;
            }
            char c;
            if (index < str.length() && ((c = str.charAt(index)) == 'e' || c == 'E')) {
                index++;
                numeric = numeric && scanInteger();
            }
        
            skipWs();
        
            return numeric && index == str.length();
        }
        void skipWs() {
            while (index < str.length() && str.charAt(index) == ' ')
                index++;
        }
    
        boolean scanInteger () {
            if (index >= str.length()) return false;
            char c = str.charAt(index);
            if (c == '+' || c == '-')
                index++;
            return scanUnsigned();
        }
    
        boolean scanUnsigned() {
            int old = index;
            while (index < str.length() && Character.isDigit(str.charAt(index)))
                index++;
            return index > old;
        }
        
    }
}
